/**
 * <p>Original Author: Daniel Freeman    http://www.danielfreeman.asia</p>
 *
 * <p>Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:</p>
 *
 * <p>The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.</p>
 *
 * <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS' OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.</p>
 *
 * <p>Licensed under The MIT License</p>
 * <p>Redistributions of files must retain the above copyright notice.</p>
 * <p>You are permitted to modify and enhance this code,</p>
 * <p>As a courtesy, please credit the original author, Daniel Freeman   http://www.danielfreeman.asia</p>
 */

// 

package
{
	[SWF(frameRate="60")]	
	
	import com.adobe.utils.*;
	
	import flash.display.Bitmap;
	import flash.display.Sprite;
	import flash.display.Stage3D;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DBlendFactor;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DTextureFormat;
	import flash.display3D.Context3DTriangleFace;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.Program3D;
	import flash.display3D.VertexBuffer3D;
	import flash.display3D.textures.Texture;
	import flash.events.Event;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;

	
	public class Flash3dExperiment7a extends Sprite {
		
		protected static const INDICES:Vector.<uint> =
			
			Vector.<uint> ([
				0,	1,	2,		0,	2,	3,
				6,	5,	4,		7,	6,	4,
				8,	9,	10,		8,	10,	11,
				14,	13,	12,		15,	14,	12,
				18,	17,	16,		19,	18,	16,
				20,	21,	22,		20,	22,	23
			]);
		
		protected static const IR2:Number = 1/Math.sqrt(2);
		
		protected static const IR3:Number = 1/Math.sqrt(3);
		
		// Vertices and normals for each face
		protected static const VERTICES:Vector.<Number> = 
			
			Vector.<Number> ([
			//	X,		Y,		Z,		nX,		nY,		nZ
				-1.0,	-1.0,	-1.0,	0,		0,		-1.0,
				1.0,	-1.0,	-1.0,	0,		0,		-1.0,
				1.0,	1.0,	-1.0,	0,		0,		-1.0,
				-1.0,	1.0,	-1.0,	0,		0,		-1.0,
				
				-1.0,	-1.0,	1.0,	0,		0,		1.0,
				1.0,	-1.0,	1.0,	0,		0,		1.0,
				1.0,	1.0,	1.0,	0,		0,		1.0,
				-1.0,	1.0,	1.0,	0,		0,		1.0,
				
				-1.0,	-1.0,	-1.0,	-1.0,	0,		0,
				-1.0,	1.0,	-1.0,	-1.0,	0,		0,
				-1.0,	1.0,	1.0,	-1.0,	0,		0,
				-1.0,	-1.0,	1.0,	-1.0,	0,		0,
				
				1.0,	-1.0,	-1.0,	1.0,	0,		0,
				1.0,	1.0,	-1.0,	1.0,	0,		0,
				1.0,	1.0,	1.0,	1.0,	0,		0,
				1.0,	-1.0,	1.0,	1.0,	0,		0,
				
				-1.0,	-1.0,	-1.0,	0,		-1,		0,
				1.0,	-1.0,	-1.0,	0,		-1,		0,
				1.0,	-1.0,	1.0,	0,		-1,		0,
				-1.0,	-1.0,	1.0,	0,		-1,		0,
				
				-1.0,	1.0,	-1.0,	0,		1.0,	0,
				1.0,	1.0,	-1.0,	0,		1.0,	0,
				1.0,	1.0,	1.0,	0,		1.0,	0,
				-1.0,	1.0,	1.0,	0,		1.0,	0,
			]);
		
		protected static const N:int = 6;
		
		// The Usual Stage3D stuff
		protected var _context3D:Context3D;
		protected var _shaderProgram:Program3D;
		protected var _vertexBuffer:VertexBuffer3D;
		protected var _indexBuffer:IndexBuffer3D;
		
		// Transformation matrices
		protected var _modelMatrix:Matrix3D = new Matrix3D();
		protected var _projectionMatrix:PerspectiveMatrix3D = new PerspectiveMatrix3D();
		
		protected var _t:Number = 0;

		public function Flash3dExperiment7a(screen:Sprite = null) {
			if (screen)
				screen.addChild(this);
			
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			
			stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
			stage.stage3Ds[0].requestContext3D();
		}
		
		
		protected function onContext3DCreate(event:Event):void {				
			_context3D = Stage3D(event.currentTarget).context3D;
			
			if (!_context3D)
				return;
			
			_context3D.enableErrorChecking = true;
			initialiseData();
		}
		
		
		protected function initialiseData():void {			
			
			// Back Buffer
			_context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 1, true);
			_context3D.setCulling(Context3DTriangleFace.FRONT);
			
			// Upload indexes
			_indexBuffer = _context3D.createIndexBuffer( INDICES.length );
			_indexBuffer.uploadFromVector(INDICES, 0, INDICES.length );
			
			// Upload vertex data
			_vertexBuffer = _context3D.createVertexBuffer( VERTICES.length/N, N); 
			_vertexBuffer.uploadFromVector(VERTICES, 0, VERTICES.length/N);
			
			// A simple vertex shader which does a 3D transformation
			var vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
			vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
				// 4x4 matrix multiply
				"m44 op, va0, vc0\n" +		// Vertex
				"m44 v0, va1, vc0\n" //+		// Normal
			);			
			
			// A simple fragment shader which will use the vertex position as a colour
			var fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
			fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
				"nrm ft0.xyz, v0 \n" +					// Normalise interpolated normal
				"mov ft0.w, fc0.w \n" +					
				"dp3 ft0, ft0, fc1\n" +					// Dot product between half angle and normal
				"sat ft0, ft0\n" +
				"mul oc, ft0, fc0"
			);
			
			// Combine shaders then upload to the GPU
			_shaderProgram = _context3D.createProgram();
			_shaderProgram.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
			
			// Perspective transformation
			_projectionMatrix.perspectiveFieldOfViewLH(45.0*Math.PI/180,stage.stageWidth/stage.stageHeight,0.1,1000.0);
			
			// Start animating
			addEventListener(Event.ENTER_FRAME,enterFrame);
		}
		
		
		protected function enterFrame(event:Event):void {
			_context3D.clear();

			_context3D.setVertexBufferAt( 0, _vertexBuffer,  0, Context3DVertexBufferFormat.FLOAT_3 );
			_context3D.setVertexBufferAt( 1, _vertexBuffer,  3, Context3DVertexBufferFormat.FLOAT_3 );
			
			_context3D.setProgram ( _shaderProgram );
			
			// Update Rotation
			_modelMatrix.identity();
			_modelMatrix.appendScale(0.5, 0.5, 0.5);
			_modelMatrix.appendRotation(_t*0.7, Vector3D.Y_AXIS);
			_modelMatrix.appendRotation(_t*0.6, Vector3D.X_AXIS);
			_modelMatrix.appendRotation(_t*1.0, Vector3D.Z_AXIS);
			_modelMatrix.appendTranslation(0, 0, 3);
			_modelMatrix.append(_projectionMatrix);
			
			_t+=4.0;
			
			_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _modelMatrix, true);
			_context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([ 0.5, 0.5, 0.5, 1.0 ]) ); // Zero
			_context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.<Number>([ IR2, 0.0, IR2, 1.0 ]) );	// Spot Lighting Direction Vector	

			_context3D.drawTriangles( _indexBuffer);
			
			_context3D.present();
		}

	}
}